﻿#include "PTWindowImpl.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ShellAPI.h>

void PTWindowImpl::HandleGet(uint32_t *pChanged, PTHandleDisplay *pHandleDisplay, PTHandleWindow *pHandleWindow)
{
	::PTSpinLockEnterSleep(&m_HandleSpinLock);//写者较读者优先
	if (((*pChanged) = m_bHandleChanged) != 0U)
	{
		m_bHandleChanged = 0U;
		(*pHandleDisplay) = reinterpret_cast<HINSTANCE>(::GetWindowLongPtrW(m_HandleWindow, GWLP_HINSTANCE));//Win32中并不存在Display的概念，只是为了和Vulkan中一致
		(*pHandleWindow) = m_HandleWindow;
	}
	::PTSpinLockLeave(&m_HandleSpinLock);
}


void PTWindowImpl::SizeGet(uint32_t *pChanged, uint32_t *pWidth, uint32_t *pHeight, uint32_t *pLayer)
{
	::PTSpinLockEnterSleep(&m_SizeSpinLock);//写者较读者优先
	if (((*pChanged) = m_bSizeChanged) != 0U)
	{
		m_bSizeChanged = 0U;
		(*pWidth) = m_Width;
		(*pHeight) = m_Height;
		(*pLayer) = 1U;
	}
	::PTSpinLockLeave(&m_SizeSpinLock);
}

void PTWindowImpl::HandleSet(HWND HandleWindow)
{
	::PTSpinLockEnterBusy(&m_HandleSpinLock);//写者较读者优先
	if (m_HandleWindow != HandleWindow)
	{
		m_bHandleChanged = 1U;
		m_HandleWindow = HandleWindow;
	}
	::PTSpinLockLeave(&m_HandleSpinLock);
}

void PTWindowImpl::SizeSet(uint32_t width, uint32_t Height)
{
	::PTSpinLockEnterBusy(&m_SizeSpinLock);//写者较读者优先
	if (m_Width != width || m_Height != Height)
	{
		m_bSizeChanged = 1U;
		m_Width = width;
		m_Height = Height;
	}
	::PTSpinLockLeave(&m_SizeSpinLock);
}

void PTWindowImpl::Construct()
{
	//Handle
	::PTSpinLockCreate(&m_HandleSpinLock);
	m_bHandleChanged = 0U;
	m_HandleWindow = NULL;
	//Size
	::PTSpinLockCreate(&m_SizeSpinLock);
	m_bSizeChanged = 0U;
	m_Width = 0U;
	m_Height = 0U;
}

void PTWindowImpl::Destruct()
{
	::PTSpinLockDestory(&m_HandleSpinLock);
	::PTSpinLockDestory(&m_SizeSpinLock);
}

void PTWindowImpl::HandleSetAndDestory(HWND HandleWindow)
{
	assert(m_HandleWindow == HandleWindow);
	::PTSpinLockEnterBusy(&m_HandleSpinLock);//写者较读者优先
	if (m_HandleWindow != NULL)
	{
		m_bHandleChanged = 1U;
		m_HandleWindow = NULL;
		BOOL wbResult = ::DestroyWindow(HandleWindow); //DestroyWindow不允许其它线程调用
		assert(wbResult != 0);
	}
	::PTSpinLockLeave(&m_HandleSpinLock);
}

void PTWindowImpl::HandleGetAndDestory()
{
	::PTSpinLockEnterSleep(&m_HandleSpinLock);//写者较读者优先
	if (m_HandleWindow != NULL)
	{
		BOOL wbResult = ::PostMessageW(m_HandleWindow, (WM_APP + 13U), 0, 0); //DestroyWindow不允许其它线程调用
		assert(wbResult != 0);
	}
	::PTSpinLockLeave(&m_HandleSpinLock);
}

static unsigned __stdcall PTInvokeMain(void *pVoid);

static LRESULT CALLBACK PTWndProc_Editor(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

static LRESULT CALLBACK PTWndProc_XR(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

static LRESULT CALLBACK PTWndProc_Desktop(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

#include "PTResource.h"

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int nCmdShow)
{
	PTWindowImpl g_WindowImpl;
	g_WindowImpl.Construct();

	HWND hWnd;
	{
		LPWSTR Editor = ::wcsstr(lpCmdLine, L"-PTEditor");
		LPWSTR XR = ::wcsstr(lpCmdLine, L"-PTXR");
		if (Editor != 0)
		{
			//编辑器模式
			//编辑器进程和运行时进程分离
			//Visual Studio的3D场景(.fbx)编辑器
			//"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.exe"
			//"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\Extensions\Microsoft\VsGraphics\vsgraphics.exe"
			LPWSTR EditorWindowName = ::wcsstr(Editor, L"-PTWindow");
			if (EditorWindowName == NULL)
			{
				hWnd = NULL;
			}
			else
			{
				EditorWindowName += 9;
				if ((*EditorWindowName) != L'=')
				{
					hWnd = NULL;
				}
				else
				{
					EditorWindowName += 1;
					HWND hParent;
#ifdef _WIN64
					hParent = reinterpret_cast<HWND>(static_cast<uintptr_t>(::_wtoi64(EditorWindowName)));
#else
					hParent = reinterpret_cast<HWND>(static_cast<uintptr_t>(::_wtoi(EditorWindowName)));
#endif
					if (::IsWindow(hParent) == FALSE)
					{
						hWnd = NULL;
					}
					else
					{
						ATOM hAtom;
						WNDCLASSEXW wc;
						wc.cbSize = sizeof(WNDCLASSEXW);
						wc.style = 0U;
						wc.lpfnWndProc = ::PTWndProc_Editor;
						wc.cbClsExtra = 0;
						wc.cbWndExtra = sizeof(void *);
						wc.hInstance = hInstance;
						wc.hIcon = ::LoadIconW(hInstance, MAKEINTRESOURCE(IDI_PTICON));
						wc.hCursor = ::LoadCursorW(NULL, IDC_ARROW);
						wc.hbrBackground = NULL;
						wc.lpszMenuName = NULL;
						wc.lpszClassName = { L"PTClass:0XFFFFFFFF -PTEditor" };
						wc.hIconSm = wc.hIcon;
						hAtom = ::RegisterClassExW(&wc);
						assert(hAtom != 0);

						RECT rect;
						::GetClientRect(hParent, &rect);

						hWnd = ::CreateWindowExW(0U, MAKEINTATOM(hAtom), L"PTWindow -PTEditor", WS_CHILD | WS_VISIBLE, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hParent, NULL, hInstance, static_cast<PTWindowImpl *>(&g_WindowImpl));
					}
				}

			}
		}
		else if (XR != 0)
		{
			//XR模式
			ATOM hAtom;
			WNDCLASSEXW wc;
			wc.cbSize = sizeof(WNDCLASSEXW);
			wc.style = 0U;
			wc.lpfnWndProc = ::PTWndProc_XR;
			wc.cbClsExtra = 0;
			wc.cbWndExtra = sizeof(void *);
			wc.hInstance = hInstance;
			wc.hIcon = ::LoadIconW(hInstance, MAKEINTRESOURCE(IDI_PTICON));
			wc.hCursor = ::LoadCursorW(NULL, IDC_ARROW);
			wc.hbrBackground = NULL;
			wc.lpszMenuName = NULL;
			wc.lpszClassName = { L"PTClass:0XFFFFFFFF -PTXR" };
			wc.hIconSm = wc.hIcon;
			hAtom = ::RegisterClassExW(&wc);
			assert(hAtom != 0);

			hWnd = ::CreateWindowExW(0U, MAKEINTATOM(hAtom), L"PTWindow -PTXR", WS_POPUP, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, static_cast<PTWindowImpl *>(&g_WindowImpl));
		}
		else
		{
			//独立模式
			ATOM hAtom;
			WNDCLASSEXW wc;
			wc.cbSize = sizeof(WNDCLASSEXW);
			wc.style = 0U;
			wc.lpfnWndProc = ::PTWndProc_Desktop;
			wc.cbClsExtra = 0;
			wc.cbWndExtra = sizeof(void *);
			wc.hInstance = hInstance;
			wc.hIcon = ::LoadIconW(hInstance, MAKEINTRESOURCE(IDI_PTICON));
			wc.hCursor = ::LoadCursorW(NULL, IDC_ARROW);
			wc.hbrBackground = NULL;
			wc.lpszMenuName = NULL;
			wc.lpszClassName = { L"PTClass:0XFFFFFFFF" };
			wc.hIconSm = wc.hIcon;
			hAtom = ::RegisterClassExW(&wc);
			assert(hAtom != 0);

			HWND hDesktop = ::GetDesktopWindow();
			HMONITOR hMonitor = ::MonitorFromWindow(hDesktop, MONITOR_DEFAULTTONEAREST);
			MONITORINFOEXW MonitorInfo;
			MonitorInfo.cbSize = sizeof(MONITORINFOEXW);
			BOOL bRet = ::GetMonitorInfoW(hMonitor, &MonitorInfo);
			assert(bRet != 0);

			hWnd = ::CreateWindowExW(WS_EX_APPWINDOW, MAKEINTATOM(hAtom), L"爱国者引擎", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, MonitorInfo.rcWork.left, MonitorInfo.rcWork.top, MonitorInfo.rcWork.right - MonitorInfo.rcWork.left, MonitorInfo.rcWork.bottom - MonitorInfo.rcWork.top, hDesktop, NULL, hInstance, static_cast<PTWindowImpl *>(&g_WindowImpl));

			//HWND hWnd = ::CreateWindowExW(WS_EX_APPWINDOW, MAKEINTATOM(atom), L"PTWindow", WS_POPUP | WS_VISIBLE, MonitorInfo.rcWork.left, MonitorInfo.rcWork.top, MonitorInfo.rcWork.right, MonitorInfo.rcWork.bottom, hDesktop, NULL, hInstance, NULL);
		}
	}

	if (hWnd == NULL)
		return 1;

	g_WindowImpl.HandleSet(hWnd);

	bool bResult;
	PTThread hThreadInvoke;
	bResult = ::PTThreadCreate(&PTInvokeMain, static_cast<PTWindowImpl *>(&g_WindowImpl), &hThreadInvoke);
	assert(bResult);

	MSG msg;
	BOOL wbResult;
	while ((wbResult = ::GetMessageW(&msg, NULL, 0, 0)) != 0)
	{
		assert(wbResult != -1);

		::TranslateMessage(&msg);
		::DispatchMessageW(&msg);
	}
	assert(msg.message == WM_QUIT);

	//确保栈中的内存 PTInvokeParam ParamInvoke 在PTInvokeMain的整个生命期内是有效的
	bResult = ::PTThreadWait(&hThreadInvoke);
	assert(bResult);

	g_WindowImpl.Destruct();
	return static_cast<int>(msg.wParam);
}

static LRESULT CALLBACK PTWndProc_Editor(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE:
	{
		::CREATESTRUCT *pCreateStruct = reinterpret_cast<::CREATESTRUCT *>(lParam);
		::SetWindowLongPtrW(hWnd, 0U, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));
		return 0;
	}
	case WM_DESTROY:
	{
		::PostQuitMessage(0U);
		return 0;
	}
	case WM_SIZE:
	{
		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->SizeSet(static_cast<uint32_t>(LOWORD(lParam)), static_cast<uint32_t>(HIWORD(lParam)));
		return 0;
	}
	//父窗口关闭
	case WM_CLOSE:
	{
		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->HandleSetAndDestory(hWnd);
		return 0;
	}
	//PTInvokeMain线程关闭
	case (WM_APP + 13U):
	{
		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->HandleSetAndDestory(hWnd);//PTInvokeMain线程中的HandleGetAndDestory并不会等待窗口过程返回，因此并不会发生死锁
		return 0;
	}
	default:
	{
		return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	}
}

static LRESULT CALLBACK PTWndProc_XR(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE:
	{
		CREATESTRUCT *pCreateStruct = reinterpret_cast<CREATESTRUCT *>(lParam);
		::SetWindowLongPtrW(hWnd, 0U, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));

		//添加任务栏图标
		NOTIFYICONDATAW nid;
		nid.cbSize = sizeof(NOTIFYICONDATAW);
		nid.hWnd = hWnd;
		nid.uID = 7U;
		nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_INFO | NIF_SHOWTIP;
		nid.uCallbackMessage = (WM_APP + 7U);
		nid.hIcon = reinterpret_cast<HICON>(::GetClassLongPtrW(hWnd, GCLP_HICONSM));
		::wcscpy_s(nid.szTip, 64U, L"爱国者引擎（虚拟现实模式）");
		::wcscpy_s(nid.szInfo, 200U, L"右键双击退出");
		nid.uVersion = NOTIFYICON_VERSION_4;
		::wcscpy_s(nid.szInfoTitle, 48U, L"爱国者引擎（虚拟现实模式）运行中");
		nid.dwInfoFlags = NIIF_NONE;
		BOOL wbResult = ::Shell_NotifyIconW(NIM_ADD, &nid);
		assert(wbResult != 0);

		return 0;
	}
	case WM_DESTROY:
	{
		//删除任务栏图标
		NOTIFYICONDATAW nid;
		nid.cbSize = sizeof(NOTIFYICONDATAW);
		nid.hWnd = hWnd;
		nid.uID = 7U;
		BOOL wbResult = ::Shell_NotifyIconW(NIM_DELETE, &nid);
		assert(wbResult != 0);

		::PostQuitMessage(0U);

		return 0;
	}
	//窗口标题栏系统菜单关闭
	case WM_CLOSE:
	{
		assert(0);
		return 0;
	}
	//任务栏关闭
	case (WM_APP + 7U):
	{
		switch (LOWORD(lParam))//系统栏消息
		{
		case WM_RBUTTONDBLCLK://接收系统栏消息的窗口可以没有CS_DBLCLKS
		{
			PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
			assert(pSingleton != NULL);
			pSingleton->HandleSetAndDestory(hWnd);
			return 0;
		}
		default:
		{
			return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
		}
		}
	}
	//PTInvokeMain线程关闭
	case (WM_APP + 13U):
	{
		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->HandleSetAndDestory(hWnd);//PTInvokeMain线程中的HandleGetAndDestory并不会等待窗口过程返回，因此并不会发生死锁
		return 0;
	}
	default:
	{
		return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	}
}

static LRESULT CALLBACK PTWndProc_Desktop(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE:
	{
		CREATESTRUCT *pCreateStruct = reinterpret_cast<CREATESTRUCT *>(lParam);
		::SetWindowLongPtrW(hWnd, 0U, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));
		return 0;
	}
	case WM_DESTROY:
	{
		::PostQuitMessage(0U);
		return 0;
	}
	case WM_SETFOCUS:
	{
		BOOL bRet;
		HMONITOR hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
		MONITORINFOEXW MonitorInfo;
		MonitorInfo.cbSize = sizeof(MONITORINFOEXW);
		bRet = ::GetMonitorInfoW(hMonitor, &MonitorInfo);
		assert(bRet != 0);
		bRet = ::SetWindowPos(hWnd, NULL, MonitorInfo.rcWork.left, MonitorInfo.rcWork.top, MonitorInfo.rcWork.right - MonitorInfo.rcWork.left, MonitorInfo.rcWork.bottom - MonitorInfo.rcWork.top, SWP_NOZORDER);
		assert(bRet != 0);
		return 0;
	}
	case WM_SIZE:
	{
		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->SizeSet(static_cast<uint32_t>(LOWORD(lParam)), static_cast<uint32_t>(HIWORD(lParam)));
		return 0;
	}
	//窗口标题栏系统菜单关闭
	case WM_CLOSE:
	{
		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->HandleSetAndDestory(hWnd);
		return 0;
	}
	//PTInvokeMain线程关闭
	case (WM_APP + 13U):
	{
		PTWindowImpl *pSingleton = reinterpret_cast<PTWindowImpl *>(::GetWindowLongPtrW(hWnd, 0U));
		assert(pSingleton != NULL);
		pSingleton->HandleSetAndDestory(hWnd);//PTInvokeMain线程中的HandleGetAndDestory并不会等待窗口过程返回，因此并不会发生死锁
		return 0;
	}
	default:
	{
		return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	}
}

#include "../../../Public/Main/PTMain.h"

static unsigned __stdcall PTInvokeMain(void *pVoid)
{
	PTWindowImpl *pWindow = static_cast<PTWindowImpl *>(pVoid);
	
	::PTMain(static_cast<IPTWindow *>(pWindow));
	
	pWindow->HandleGetAndDestory();
	return 0U;//::PTThreadWait
}